home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / src_1218.zip / SMTPSERV.C < prev    next >
C/C++ Source or Header  |  1991-06-01  |  23KB  |  1,003 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  */
  4. #include <stdio.h>
  5. #include <time.h>
  6. #ifdef UNIX
  7. #include <sys/types.h>
  8. #endif
  9. #if    defined(__STDC__) || defined(__TURBOC__)
  10. #include <stdarg.h>
  11. #endif
  12. #include <ctype.h>
  13. #include <setjmp.h>
  14. #include "global.h"
  15. #include "mbuf.h"
  16. #include "cmdparse.h"
  17. #include "socket.h"
  18. #include "iface.h"
  19. #include "proc.h"
  20. #include "smtp.h"
  21. #include "commands.h"
  22. #include "dirutil.h"
  23. #include "mailbox.h"
  24. #include "bm.h"
  25. #include "domain.h"
  26.  
  27. char *Days[7] = {  "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  28. char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
  29.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  30.  
  31.  
  32.  
  33. static struct list *expandalias __ARGS((struct list **head,char *user));
  34. static int  getmsgtxt __ARGS((struct smtpsv *mp));
  35. static struct smtpsv *mail_create __ARGS((void));
  36. static void mail_clean __ARGS((struct smtpsv *mp));
  37. static int mailit __ARGS((FILE *data,char *from,struct list *tolist));
  38. static int router_queue __ARGS((FILE *data,char *from,struct list *to));
  39. static void smtplog __ARGS((char *fmt,...));
  40. static void smtpserv __ARGS((int s,void *unused,void *p));
  41. static int mailuser __ARGS((FILE *data,char *from,char *to));
  42.  
  43. /* Command table */
  44. static char *commands[] = {
  45.     "helo",
  46. #define    HELO_CMD    0
  47.     "noop",
  48. #define    NOOP_CMD    1
  49.     "mail from:",
  50. #define    MAIL_CMD    2
  51.     "quit",
  52. #define    QUIT_CMD    3
  53.     "rcpt to:",
  54. #define    RCPT_CMD    4
  55.     "help",
  56. #define    HELP_CMD    5
  57.     "data",
  58. #define    DATA_CMD    6
  59.     "rset",
  60. #define    RSET_CMD    7
  61.     "expn",
  62. #define EXPN_CMD    8
  63.     NULLCHAR
  64. };
  65.  
  66. /* Reply messages */
  67. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
  68. static char Banner[] = "220 %s SMTP ready\n";
  69. static char Closing[] = "221 Closing\n";
  70. static char Ok[] = "250 Ok\n";
  71. static char Reset[] = "250 Reset state\n";
  72. static char Sent[] = "250 Sent\n";
  73. static char Ourname[] = "250 %s, Share and Enjoy!\n";
  74. static char Enter[] = "354 Enter mail, end with .\n";
  75. static char Ioerr[] = "452 Temp file write error\n";
  76. static char Badcmd[] = "500 Command unrecognized\n";
  77. static char Lowmem[] = "421 System overloaded, try again later\n";
  78. static char Syntax[] = "501 Syntax error\n";
  79. static char Needrcpt[] = "503 Need RCPT (recipient)\n";
  80. static char Unknown[] = "550 <%s> address unknown\n";
  81. static char Noalias[] = "550 No alias for <%s>\n";
  82.  
  83. static int Ssmtp = -1; /* prototype socket for service */
  84.  
  85. /* Start up SMTP receiver service */
  86. int
  87. smtp1(argc,argv,p)
  88. int argc;
  89. char *argv[];
  90. void *p;
  91. {
  92.     struct sockaddr_in lsocket;
  93.     int s;
  94.  
  95.     if(Ssmtp != -1){
  96.         return 0;
  97.     }
  98.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  99.     chname(Curproc,"SMTP listener");
  100.  
  101.     lsocket.sin_family = AF_INET;
  102.     lsocket.sin_addr.s_addr = INADDR_ANY;
  103.     if(argc < 2)
  104.         lsocket.sin_port = IPPORT_SMTP;
  105.     else
  106.         lsocket.sin_port = atoi(argv[1]);
  107.  
  108.     Ssmtp = socket(AF_INET,SOCK_STREAM,0);
  109.     bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
  110.     listen(Ssmtp,1);
  111.     for(;;){
  112.         if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
  113.             break;    /* Service is shutting down */
  114.  
  115.         if(availmem() < Memthresh){
  116.             usprintf(s,Lowmem);
  117.             shutdown(s,1);
  118.         } else {
  119.             /* Spawn a server */
  120.             newproc("SMTP server",2048,smtpserv,s,NULL,NULL,0);
  121.         }
  122.     }
  123.     return 0;
  124. }
  125.  
  126. /* Shutdown SMTP service (existing connections are allowed to finish) */
  127. int
  128. smtp0(argc,argv,p)
  129. int argc;
  130. char *argv[];
  131. void *p;
  132. {
  133.     close_s(Ssmtp);
  134.     Ssmtp = -1;
  135.     return 0;
  136. }
  137.  
  138. static void
  139. smtpserv(s,unused,p)
  140. int s;
  141. void *unused;
  142. void *p;
  143. {
  144.     struct smtpsv *mp;
  145.     char **cmdp,buf[LINELEN],*arg,*cp,*cmd,*newaddr;
  146.     struct list *ap,*list;
  147.     int cnt;
  148.     char address_type;
  149.  
  150.     sockmode(s,SOCK_ASCII);
  151.     sockowner(s,Curproc);        /* We own it now */
  152.     log(s,"open SMTP");
  153.  
  154.     if((mp = mail_create()) == NULLSMTPSV){
  155.         printf(Nospace);
  156.         log(s,"close SMTP - no space");
  157.         close_s(s);
  158.         return;
  159.     }
  160.     mp->s = s;
  161.  
  162.     (void) usprintf(s,Banner,Hostname);
  163.  
  164. loop:    if ((cnt = recvline(s,buf,sizeof(buf))) == -1) {
  165.         /* He closed on us */
  166.         goto quit;
  167.     }
  168.     if(cnt < 4){
  169.         /* Can't be a legal command */
  170.         usprintf(mp->s,Badcmd);
  171.         goto loop;
  172.     }    
  173.     rip(buf);
  174.     cmd = buf;
  175.  
  176.     /* Translate entire buffer to lower case */
  177.     for(cp = cmd;*cp != '\0';cp++)
  178.         *cp = tolower(*cp);
  179.  
  180.     /* Find command in table; if not present, return syntax error */
  181.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  182.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  183.             break;
  184.     if(*cmdp == NULLCHAR){
  185.         (void) usprintf(mp->s,Badcmd);
  186.         goto loop;
  187.     }
  188.     arg = &cmd[strlen(*cmdp)];
  189.     /* Skip spaces after command */
  190.     while(*arg == ' ')
  191.         arg++;
  192.     /* Execute specific command */
  193.     switch(cmdp-commands) {
  194.     case HELO_CMD:
  195.         free(mp->system);
  196.         mp->system = strdup(arg);
  197.         (void) usprintf(mp->s,Ourname,Hostname);
  198.         break;
  199.     case NOOP_CMD:
  200.         (void) usprintf(mp->s,Ok);
  201.         break;
  202.     case MAIL_CMD:
  203.         if((cp = getname(arg)) == NULLCHAR){
  204.             (void) usprintf(mp->s,Syntax);
  205.             break;
  206.         }
  207.         free(mp->from);
  208.         mp->from = strdup(cp);
  209.         (void) usprintf(mp->s,Ok);
  210.         break;
  211.     case QUIT_CMD:
  212.         (void) usprintf(mp->s,Closing);
  213.         goto quit;
  214.     case RCPT_CMD:    /* Specify recipient */
  215.         if((cp = getname(arg)) == NULLCHAR){
  216.             (void) usprintf(mp->s,Syntax);
  217.             break;
  218.         }
  219.  
  220.         /* rewrite address if possible */
  221.         if((newaddr = rewrite_address(cp)) != NULLCHAR) {
  222.             strcpy(buf,newaddr);
  223.             cp = buf;
  224.             free(newaddr);
  225.         }
  226.  
  227.         /* check if address is ok */
  228.         if ((address_type = validate_address(cp)) == BADADDR) {
  229.             (void) usprintf(mp->s,Unknown,cp);
  230.             break;
  231.         }
  232.         /* if a local address check for an alias */
  233.         if (address_type == LOCAL)
  234.             expandalias(&mp->to, cp);
  235.         else
  236.             /* a remote address is added to the list */
  237.             addlist(&mp->to, cp, address_type);
  238.  
  239.         (void) usprintf(mp->s,Ok);
  240.         break;
  241.     case HELP_CMD:
  242.         (void) usprintf(mp->s,Help);
  243.         break;
  244.     case DATA_CMD:
  245.         if(mp->to == NULLLIST)
  246.             (void) usprintf(mp->s,Needrcpt);
  247.         else if ((mp->data = tmpfile()) == NULLFILE)
  248.             (void) usprintf(mp->s,Ioerr);
  249.          else
  250.             getmsgtxt(mp);
  251.         break;
  252.     case RSET_CMD:
  253.         del_list(mp->to);
  254.         mp->to = NULLLIST;
  255.         (void) usprintf(mp->s,Reset);
  256.         break;
  257.     case EXPN_CMD:
  258.         if (*arg == '\0') {
  259.             (void) usprintf(mp->s,Syntax);
  260.             break;
  261.         }
  262.  
  263.         list = NULLLIST;
  264.         /* rewrite address if possible */
  265.         if((newaddr = rewrite_address(arg)) != NULLCHAR)
  266.             if(strcmp(newaddr,arg) == 0) {
  267.                 free(newaddr);
  268.                 newaddr = NULLCHAR;
  269.             }
  270.             else {
  271.                 strcpy(buf,newaddr);
  272.                 arg = buf;
  273.             }
  274.         list = NULLLIST;
  275.         expandalias(&list,arg);
  276.         if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
  277.             if(newaddr == NULLCHAR) {
  278.                 (void) usprintf(mp->s,Noalias,arg);
  279.                 del_list(list);
  280.                 break;
  281.             }
  282.         ap = list;
  283.         while (ap->next != NULLLIST) {
  284.             (void) usprintf(mp->s,"250-%s\n",ap->val);
  285.             ap = ap->next;
  286.         }
  287.         usprintf(mp->s,"250 %s\n",ap->val);
  288.         del_list(list);
  289.         free(newaddr);
  290.         break;
  291.     }
  292.     goto loop;
  293.  
  294. quit:
  295.     log(mp->s,"close SMTP");
  296.     close_s(mp->s);
  297.     mail_clean(mp);
  298.     smtptick(NULL);            /* start SMTP daemon immediately */
  299. }
  300.  
  301. /* read the message text */
  302. static int
  303. getmsgtxt(mp)
  304. struct smtpsv *mp;
  305. {
  306.     char buf[LINELEN];
  307.     register char *p = buf;
  308.     long t;
  309.  
  310.     /* Add timestamp; ptime adds newline */
  311.     time(&t);
  312.     fprintf(mp->data,"Received: ");
  313.     if(mp->system != NULLCHAR)
  314.         fprintf(mp->data,"from %s ",mp->system);
  315.     fprintf(mp->data,"by %s with SMTP\n\tid AA%ld ; %s",
  316.             Hostname, get_msgid(), ptime(&t));
  317.     if(ferror(mp->data)){
  318.         (void) usprintf(mp->s,Ioerr);
  319.         return 1;
  320.     } else {
  321.         (void) usprintf(mp->s,Enter);
  322.     }
  323.     while(1) {
  324.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  325.             return 1;
  326.         }
  327.         rip(p);
  328.         /* check for end of message ie a . or escaped .. */
  329.         if (*p == '.') {
  330.             if (*++p == '\0') {
  331.                 /* Also sends appropriate response */
  332.                 if (mailit(mp->data,mp->from,mp->to) != 0)
  333.                     (void) usprintf(mp->s,Ioerr);
  334.                 else
  335.                     (void) usprintf(mp->s,Sent);
  336.                 fclose(mp->data);
  337.                 mp->data = NULLFILE;
  338.                 del_list(mp->to);
  339.                 mp->to = NULLLIST;
  340.                 return 0;
  341.             } else if (!(*p == '.' && *(p+1) == '\0'))
  342.                 p--;
  343.         }
  344.         /* for UNIX mail compatiblity */
  345.         if (strncmp(p,"From ",5) == 0)
  346.             (void) putc('>',mp->data);
  347.         /* Append to data file */
  348.         if(fprintf(mp->data,"%s\n",p) < 0) {
  349.             (void) usprintf(mp->s,Ioerr);
  350.             return 1;
  351.         }
  352.     }
  353.     return 0;
  354. }
  355.  
  356. /* Create control block, initialize */
  357. static struct smtpsv *
  358. mail_create()
  359. {
  360.     register struct smtpsv *mp;
  361.  
  362.     mp = (struct smtpsv *)callocw(1,sizeof(struct smtpsv));
  363.     mp->from = strdup("");    /* Default to null From address */
  364.     return mp;
  365. }
  366.  
  367. /* Free resources, delete control block */
  368. static void
  369. mail_clean(mp)
  370. register struct smtpsv *mp;
  371. {
  372.     if (mp == NULLSMTPSV)
  373.         return;
  374.     free(mp->system);
  375.     free(mp->from);
  376.     if(mp->data != NULLFILE)
  377.         fclose(mp->data);
  378.     del_list(mp->to);
  379.     free((char *)mp);
  380. }
  381.  
  382.  
  383. /* Given a string of the form <user@host>, extract the part inside the
  384.  * brackets and return a pointer to it.
  385.  */
  386. char *
  387. getname(cp)
  388. register char *cp;
  389. {
  390.     register char *cp1;
  391.  
  392.     if ((cp = strchr(cp,'<')) == NULLCHAR)
  393.         return NULLCHAR;
  394.     cp++;    /* cp -> first char of name */
  395.     if ((cp1 = strchr(cp,'>')) == NULLCHAR)
  396.         return NULLCHAR;
  397.     *cp1 = '\0';
  398.     return cp;
  399. }
  400.  
  401.         
  402. /* General mailit function. It takes a list of addresses which have already
  403. ** been verified and expanded for aliases. Base on the current mode the message
  404. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  405. */
  406. static int
  407. mailit(data,from,tolist)
  408. FILE *data;
  409. char *from;
  410. struct list *tolist;
  411. {
  412.     struct list *ap, *dlist = NULLLIST;
  413.     register FILE *fp;
  414.     char    mailbox[50], *cp, *host, *qhost;
  415.     int    c, fail = 0;
  416.     time_t    t;
  417.  
  418.     if ((Smtpmode & QUEUE) != 0)
  419.         return(router_queue(data,from,tolist));
  420.  
  421.     do {
  422.         qhost = NULLCHAR;
  423.         for(ap = tolist;ap != NULLLIST;ap = ap->next)
  424.             if (ap->type == DOMAIN) {
  425.                 if ((host = strrchr(ap->val,'@')) != NULLCHAR)
  426.                     host++;
  427.                 else
  428.                     host = Hostname;
  429.                 if(qhost == NULLCHAR)
  430.                          qhost = host;
  431.                 if(stricmp(qhost,host) == 0) {
  432.                     ap->type = BADADDR;
  433.                     addlist(&dlist,ap->val,0);
  434.                 }
  435.             }
  436.         if(qhost != NULLCHAR) {
  437.             rewind(data);
  438.             queuejob(data,qhost,dlist,from);
  439.             del_list(dlist);
  440.             dlist = NULLLIST;
  441.         }
  442.     } while(qhost != NULLCHAR);
  443.  
  444.     for(ap = tolist;ap != NULLLIST;ap = ap->next) {
  445.         if(ap->type != LOCAL) {
  446.             ap->type = DOMAIN;
  447.             continue;
  448.         }
  449.         rewind(data);
  450.         /* strip off host name of LOCAL addresses */
  451.         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  452.             *cp = '\0';
  453.  
  454.         /* truncate long user names */
  455.         if (strlen(ap->val) > MBOXLEN)
  456.             ap->val[MBOXLEN] = '\0';
  457.  
  458.         /* if mail file is busy save it in our smtp queue
  459.          * and let the smtp daemon try later.
  460.          */
  461.         if (mlock(Mailspool,ap->val)) {
  462.             addlist(&dlist,ap->val,0);
  463.             fail = queuejob(data,Hostname,dlist,from);
  464.             del_list(dlist);
  465.             dlist = NULLLIST;
  466.         }
  467.         else {
  468.             char buf[LINELEN];
  469.             int tocnt = 0;
  470.             sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  471. #ifndef    AMIGA
  472.             if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE) {
  473. #else
  474.             if((fp = fopen(mailbox,"r+")) != NULLFILE) {
  475.                 (void) fseek(fp, 0L, 2);
  476. #endif
  477.                 time(&t);
  478.                 fprintf(fp,"From %s %s",from,ctime(&t));
  479.                 host = NULLCHAR;
  480.                 while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  481.                     if(buf[0] == '\n'){
  482.                         if(tocnt == 0)
  483.                             fprintf(fp,"%s%s\n",
  484.                                 Hdrs[APPARTO],
  485.                                 ap->val);
  486.                         fputc('\n',fp);
  487.                         break;
  488.                     }
  489.                     fputs(buf,fp);
  490.                     rip(buf);
  491.                     switch(htype(buf)){
  492.                     case TO:
  493.                     case CC:
  494.                         ++tocnt;
  495.                         break;
  496.                     case RRECEIPT:
  497.                         if((cp = getaddress(buf,0))
  498.                            != NULLCHAR){
  499.                             free(host);
  500.                             host = strdup(cp);
  501.                         }
  502.                         break;
  503.                     }
  504.                 }
  505.                 while((c = fread(buf,1,sizeof(buf),data)) > 0)
  506.                     if(fwrite(buf,1,c,fp) != c)
  507.                         break;
  508.                 if(ferror(fp))
  509.                     fail = 1;
  510.                 else
  511.                     fprintf(fp,"\n");
  512.                 /* Leave a blank line between msgs */
  513.                 fclose(fp);
  514.                 printf("New mail arrived for %s\n",ap->val);
  515.                 if(host != NULLCHAR){
  516.                     rewind(data); /* Send return receipt */
  517.                     mdaemon(data,host,NULLLIST,0);
  518.                     free(host);
  519.                 }
  520.             } else 
  521.                 fail = 1;
  522.             (void) rmlock(Mailspool,ap->val);
  523.             if (fail)
  524.                 break;
  525.             smtplog("deliver: To: %s From: %s",ap->val,from);
  526.         }
  527.     }
  528.     return fail;
  529. }
  530.  
  531. /* Return Date/Time in Arpanet format in passed string */
  532. char *
  533. ptime(t)
  534. long *t;
  535. {
  536.     /* Print out the time and date field as
  537.      *        "DAY day MONTH year hh:mm:ss ZONE"
  538.      */
  539.     register struct tm *ltm;
  540.     static char tz[4];
  541.     static char str[40];
  542.     char *p, *getenv();
  543.     /* Read the system time */
  544.     ltm = localtime(t);
  545.  
  546.     if (*tz == '\0')
  547.         if ((p = getenv("TZ")) == NULL)
  548.             strcpy(tz,"UTC");
  549.         else
  550.             strncpy(tz,p,3);
  551.  
  552.     /* rfc 822 format */
  553.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  554.         Days[ltm->tm_wday],
  555.         ltm->tm_mday,
  556.         Months[ltm->tm_mon],
  557.         ltm->tm_year,
  558.         ltm->tm_hour,
  559.         ltm->tm_min,
  560.         ltm->tm_sec,
  561.         tz);
  562.     return(str);
  563. }
  564.  
  565. long 
  566. get_msgid()
  567. {
  568.     char sfilename[LINELEN];
  569.     char s[20];
  570.     register long sequence = 0;
  571.     FILE *sfile;
  572.  
  573.     sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  574.     sfile = fopen(sfilename,READ_TEXT);
  575.  
  576.     /* if sequence file exists, get the value, otherwise set it */
  577.     if (sfile != NULL) {
  578.         (void) fgets(s,sizeof(s),sfile);
  579.         sequence = atol(s);
  580.     /* Keep it in range of and 8 digit number to use for dos name prefix. */
  581.         if (sequence < 0L || sequence > 99999999L )
  582.             sequence = 0;
  583.         fclose(sfile);
  584.     }
  585.  
  586.     /* increment sequence number, and write to sequence file */
  587.     sfile = fopen(sfilename,WRITE_TEXT);
  588.     fprintf(sfile,"%ld",++sequence);
  589.     fclose(sfile);
  590.     return sequence;
  591. }
  592.  
  593. #ifdef    MSDOS
  594. /* Illegal characters in a DOS filename */
  595. static char baddoschars[] = "\"[]:|<>+=;,";
  596. #endif
  597.  
  598. /* test if mail address is valid */
  599. int
  600. validate_address(s)
  601. char *s;
  602. {
  603.     char *cp;
  604.     int32 addr;
  605.  
  606.     /* if address has @ in it the check dest address */
  607.     if ((cp = strrchr(s,'@')) != NULLCHAR) {
  608.         cp++;
  609.         /* 1st check if its our hostname
  610.         * if not then check the hosts file and see
  611.         * if we can resolve ther address to a know site
  612.         * or one of our aliases
  613.         */
  614.         if (strcmp(cp,Hostname) != 0) {
  615.             if ((addr = mailroute(cp)) == 0
  616.                 && (Smtpmode & QUEUE) == 0)
  617.                 return BADADDR;
  618.             if (ismyaddr(addr) == NULLIF)
  619.                 return DOMAIN;
  620.         }
  621.         
  622.         /* on a local address remove the host name part */
  623.         *--cp = '\0';
  624.     }
  625.  
  626.     /* if using an external router leave address alone */
  627.     if ((Smtpmode & QUEUE) != 0)
  628.         return LOCAL;
  629.  
  630.     /* check for the user%host hack */
  631.     if ((cp = strrchr(s,'%')) != NULLCHAR) {
  632.         *cp = '@';
  633.         cp++;
  634.         /* reroute based on host name following the % seperator */
  635.         if (mailroute(cp) == 0)
  636.             return BADADDR;
  637.         else
  638.             return DOMAIN;
  639.     }
  640.  
  641. #ifdef MSDOS    /* dos file name checks */
  642.     /* Check for characters illegal in MS-DOS file names */
  643.     for(cp = baddoschars;*cp != '\0';cp++){
  644.         if(strchr(s,*cp) != NULLCHAR)
  645.             return BADADDR;    
  646.     }
  647. #endif
  648.     return LOCAL;
  649. }
  650.  
  651. /* place a mail job in the outbound queue */
  652. int
  653. queuejob(dfile,host,to,from)
  654. FILE *dfile;
  655. char *host;
  656. struct list *to;
  657. char *from;
  658. {
  659.     FILE *fp;
  660.     struct list *ap;
  661.     char tmpstring[50], prefix[9], buf[LINELEN];
  662.     register int cnt;
  663.  
  664.     sprintf(prefix,"%ld",get_msgid());
  665.     mlock(Mailqdir,prefix);
  666.     sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  667.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  668.         (void) rmlock(Mailqdir,prefix);
  669.         return 1;
  670.     }
  671.     while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
  672.         if(fwrite(buf, 1, cnt, fp) != cnt)
  673.             break;
  674.     if(ferror(fp)){
  675.         fclose(fp);
  676.         (void) rmlock(Mailqdir,prefix);
  677.         return 1;
  678.     }
  679.     fclose(fp);
  680.     sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  681.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  682.         (void) rmlock(Mailqdir,prefix);
  683.         return 1;
  684.     }
  685.     fprintf(fp,"%s\n%s\n",host,from);
  686.     for(ap = to; ap != NULLLIST; ap = ap->next) {
  687.         fprintf(fp,"%s\n",ap->val);
  688.         smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
  689.     }
  690.     fclose(fp);
  691.     (void) rmlock(Mailqdir,prefix);
  692.     return 0;
  693. }
  694.  
  695. /* Deliver mail to the appropriate mail boxes */
  696. static int
  697. router_queue(data,from,to)
  698. FILE *data;
  699. char *from;
  700. struct list *to;
  701. {
  702.     int c;
  703.     register struct list *ap;
  704.     FILE *fp;
  705.     char tmpstring[50];
  706.     char prefix[9];
  707.  
  708.     sprintf(prefix,"%ld",get_msgid());
  709.     mlock(Routeqdir,prefix);
  710.     sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  711.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  712.         (void) rmlock(Routeqdir,prefix);
  713.         return 1;
  714.     }
  715.     rewind(data);
  716.     while((c = getc(data)) != EOF)
  717.         if(putc(c,fp) == EOF)
  718.             break;
  719.     if(ferror(fp)){
  720.         fclose(fp);
  721.         (void) rmlock(Routeqdir,prefix);
  722.         return 1;
  723.     }
  724.     fclose(fp);
  725.     sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  726.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  727.         (void) rmlock(Routeqdir,prefix);
  728.         return 1;
  729.     }
  730.     fprintf(fp,"From: %s\n",from);
  731.     for(ap = to;ap != NULLLIST;ap = ap->next) {
  732.         fprintf(fp,"To: %s\n",ap->val);
  733.     }
  734.     fclose(fp);
  735.     (void) rmlock(Routeqdir,prefix);
  736.     smtplog("rqueue job %s From: %s",prefix,from);
  737.     return 0;
  738. }
  739.  
  740. /* add an element to the front of the list pointed to by head 
  741. ** return NULLLIST if out of memory.
  742. */
  743. struct list *
  744. addlist(head,val,type)
  745. struct list **head;
  746. char *val;
  747. int type;
  748. {
  749.     register struct list *tp;
  750.  
  751.     tp = (struct list *)callocw(1,sizeof(struct list));
  752.  
  753.     tp->next = NULLLIST;
  754.  
  755.     /* allocate storage for the char string */
  756.     tp->val = strdup(val);
  757.     tp->type = type;
  758.  
  759.     /* add entry to front of existing list */
  760.     if (*head == NULLLIST)
  761.         *head = tp;
  762.     else {
  763.         tp->next = *head;
  764.         *head = tp;
  765.     }
  766.     return tp;
  767.  
  768. }
  769.  
  770. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  771. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  772.  
  773. /* check for and alias and expand alias into a address list */
  774. static struct list *
  775. expandalias(head, user)
  776. struct list **head;
  777. char *user;
  778. {
  779.     FILE *fp;
  780.     register char *s,*p;
  781.     struct rr *rrp, *rrlp;
  782.     int inalias = 0;
  783.     struct list *tp;
  784.     char buf[LINELEN];
  785.     
  786.     /* no alias file found */
  787.     if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE) {
  788.         /* Try MB, MG or MR domain name records */
  789.         rrlp = rrp = resolve_mailb(user);
  790.         while(rrp != NULLRR){
  791.             if(rrp->rdlength > 0){
  792.                 /* remove the trailing dot */
  793.                 rrp->rdata.name[rrp->rdlength-1] = '\0';
  794.                 /* replace first dot with @ if there is no @ */
  795.                 if(strchr(rrp->rdata.name,'@') == NULLCHAR
  796.                    && (p = strchr(rrp->rdata.name,'.')) !=
  797.                    NULLCHAR)
  798.                     *p = '@';
  799.                 if(strchr(rrp->rdata.name,'@') != NULLCHAR)
  800.                     tp = addlist(head,rrp->rdata.name,
  801.                              DOMAIN);
  802.                 else
  803.                     tp = addlist(head,rrp->rdata.name,
  804.                              LOCAL);
  805.                 ++inalias;
  806.             }
  807.             rrp = rrp->next;
  808.         }
  809.         free_rr(rrlp);
  810.         if(inalias)
  811.             return tp;
  812.         else
  813.             return addlist(head, user, LOCAL);
  814.     }
  815.  
  816.     while (fgets(buf,LINELEN,fp) != NULLCHAR) {
  817.         p = buf;
  818.         if ( *p == '#' || *p == '\0')
  819.             continue;
  820.         rip(p);
  821.  
  822.         /* if not in an matching entry skip continuation lines */
  823.         if (!inalias && isspace(*p))
  824.             continue;
  825.  
  826.         /* when processing an active alias check for a continuation */
  827.         if (inalias) {
  828.             if (!isspace(*p)) 
  829.                 break;    /* done */
  830.         } else {
  831.             s = p;
  832.             SKIPWORD(p);
  833.             *p++ = '\0';    /* end the alias name */
  834.             if (strcmp(s,user) != 0)
  835.                 continue;    /* no match go on */
  836.             inalias = 1;
  837.         }
  838.  
  839.         /* process the recipients on the alias line */
  840.         SKIPSPACE(p);
  841.         while(*p != '\0' && *p != '#') {
  842.             s = p;
  843.             SKIPWORD(p);
  844.             if (*p != '\0')
  845.                 *p++ = '\0';
  846.  
  847.             /* find hostname */
  848.             if (strchr(s,'@') != NULLCHAR)
  849.                 tp = addlist(head,s,DOMAIN);
  850.             else
  851.                 tp = addlist(head,s,LOCAL);
  852.             SKIPSPACE(p);
  853.         }
  854.     }
  855.     (void) fclose(fp);
  856.  
  857.     if (inalias)    /* found and processed and alias. */
  858.         return tp;
  859.  
  860.     /* no alias found treat as a local address */
  861.     return addlist(head, user, LOCAL);
  862. }
  863.  
  864. #if    defined(ANSIPROTO)
  865. static void
  866. smtplog(char *fmt, ...)
  867. {
  868.     va_list ap;
  869.     char *cp;
  870.     long t;
  871.     FILE *fp;
  872.  
  873.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  874.         return;
  875.     time(&t);
  876.     cp = ctime(&t);
  877.     rip(cp);
  878.     fprintf(fp,"%s ",cp);
  879.     va_start(ap,fmt);
  880.     vfprintf(fp,fmt,ap);
  881.     va_end(ap);
  882.     fprintf(fp,"\n");
  883.     fclose(fp);
  884. }
  885.  
  886. #else
  887.  
  888. static void
  889. smtplog(fmt,arg1,arg2,arg3,arg4)
  890. char *fmt;
  891. int arg1,arg2,arg3,arg4;
  892. {
  893.     char *cp;
  894.     long t;
  895.     FILE *fp;
  896.  
  897.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  898.         return;
  899.     time(&t);
  900.     cp = ctime(&t);
  901.     rip(cp);
  902.     fprintf(fp,"%s ",cp);
  903.     fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  904.     fprintf(fp,"\n");
  905.     fclose(fp);
  906. }
  907. #endif
  908.  
  909. /* send mail to a single user. Can be called from the ax24 mailbox or
  910. ** from the return mail function in the smtp client 
  911. */
  912. static int
  913. mailuser(data,from,to)
  914. FILE *data;
  915. char *from;
  916. char *to;
  917. {
  918.  
  919.         int address_type, ret;
  920.         struct list *tolist = NULLLIST;
  921.  
  922.         /* check if address is ok */
  923.         if ((address_type = validate_address(to)) == BADADDR) {
  924.             return 1;
  925.         }
  926.         /* if a local address check for an alias */
  927.         if (address_type == LOCAL)
  928.             expandalias(&tolist, to);
  929.         else
  930.             /* a remote address is added to the list */
  931.             addlist(&tolist, to, address_type);
  932.         ret = mailit(data,from,tolist);
  933.         del_list(tolist);
  934.         return ret;
  935.  
  936. }
  937.  
  938. /* Mailer daemon return mail mechanism */
  939. int
  940. mdaemon(data,to,lp,bounce)
  941. FILE *data;        /* pointer to rewound data file */
  942. char *to;        /* Overridden by Errors-To: line if bounce is true */
  943. struct list *lp;    /* error log for failed mail */
  944. int bounce;        /* True for failed mail, otherwise return receipt */
  945. {
  946.     time_t t;
  947.     FILE *tfile;
  948.     char buf[LINELEN], *cp, *newto = NULLCHAR;
  949.     int cnt;
  950.     if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){
  951.         while(fgets(buf,sizeof(buf),data) != NULLCHAR) {
  952.             if(buf[0] == '\n')
  953.                 break;
  954.             /* Look for Errors-To: */
  955.             if(htype(buf) == ERRORSTO &&
  956.                (cp = getaddress(buf,0)) != NULLCHAR){
  957.                 free(newto);
  958.                 newto = strdup(cp);
  959.                 break;
  960.             }
  961.         }
  962.         if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
  963.            to == NULLCHAR))
  964.             return -1;
  965.         rewind(data);
  966.     }
  967.     if((tfile = tmpfile()) == NULLFILE)
  968.         return -1;
  969.     time(&t);
  970.     fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&t));
  971.     fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  972.     fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
  973.         Hdrs[FROM],Hostname);
  974.     fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
  975.     fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
  976.         bounce ? "Failed mail" : "Return receipt");
  977.     if(bounce) {
  978.         fprintf(tfile,"  ===== transcript follows =====\n\n");
  979.         for (; lp != NULLLIST; lp = lp->next)
  980.             fprintf(tfile,"%s\n",lp->val);
  981.         fprintf(tfile,"\n");
  982.     }
  983.     fprintf(tfile,"  ===== %s follows ====\n",
  984.         bounce ? "Unsent message" : "Message header");
  985.  
  986.     while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  987.         if(buf[0] == '\n')
  988.             break;
  989.         fputs(buf,tfile);
  990.     }
  991.     if(bounce){
  992.         fputc('\n',tfile);
  993.         while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
  994.             fwrite(buf,1,cnt,tfile);
  995.     }
  996.     fseek(tfile,0L,0);
  997.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  998.     (void) mailuser(tfile,"",newto != NULLCHAR ? newto : to);
  999.     fclose(tfile);
  1000.     free(newto);
  1001.     return 0;
  1002. }
  1003.